package com.example.hydrogengenerator.ui.frag

import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothGatt
import android.content.Context
import android.content.Intent
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleScanCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.exception.BleException
import com.example.hydrogengenerator.R
import com.example.hydrogengenerator.base.BaseFragment
import com.example.hydrogengenerator.databinding.FragHomeBinding
import com.example.hydrogengenerator.model.HomeModel
import com.example.hydrogengenerator.utils.BleUtils
import com.example.hydrogengenerator.utils.PermissionUtils
import com.permissionx.guolindev.PermissionX
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import me.hgj.jetpackmvvm.ext.nav
import me.hgj.jetpackmvvm.ext.navigateAction
import me.hgj.jetpackmvvm.ext.util.loge
import java.lang.ref.WeakReference
import java.util.concurrent.LinkedBlockingDeque

class HomeFrag : BaseFragment<HomeModel, FragHomeBinding>() {

    val bleUtils: BleUtils by lazy { BleUtils.getInstance() }
    val dataQueue: LinkedBlockingDeque<ByteArray> = LinkedBlockingDeque()

    private var i = 0
    private var j = 0
    var time = 30 //min
        set(value) {
            field = value
            val min = value % 60
            val hour = value / 60
            mDatabind.tvSetTime.text = String.format("%02d:%02d", hour, min)
        }

    override fun layoutId(): Int = R.layout.frag_home

    override fun initView(savedInstanceState: Bundle?) {
        mDatabind.click = Click()
        mDatabind.model = mViewModel
    }

    override fun createObserver() {
        super.createObserver()
        appViewModel.factorySettingInfo.observe(this) {
            mViewModel.factorySettingData.value = it
        }
        appViewModel.settingInfo.observe(this) {
            mViewModel.SettingBean.value = it
        }
        eventViewModel.spo2Data.onEach { wave ->
            mDatabind.ecgPicture.pointIntArr[j] = wave
            j++
            if (j >= 500) {
                j = 0
            }
            mDatabind.ecgPicture.mTag = true
        }.launchIn(lifecycleScope)

        eventViewModel.respirationRate.onEach { respirationRate ->
            mDatabind.tvRespirationRate.text = respirationRate.toString()
        }.flowOn(Dispatchers.Main).launchIn(lifecycleScope)

        eventViewModel.inhalationMark.onEach { inhalationMark ->

        }.flowOn(Dispatchers.Main).launchIn(lifecycleScope)

        eventViewModel.spo2.onEach { spo2 ->
            mDatabind.tvSpo2.text = spo2.toString()
        }.flowOn(Dispatchers.Main).launchIn(lifecycleScope)

        eventViewModel.pulse.onEach { pulse ->
            mDatabind.tvPulseRate.text = pulse.toString()
        }.flowOn(Dispatchers.Main).launchIn(lifecycleScope)

        eventViewModel.pulseWaveData.onEach { wave ->
            mDatabind.ecgPicture2.pointIntArr[i] = wave
            i++
            if (i >= 500) {
                i = 0
            }
            mDatabind.ecgPicture2.mTag = true
        }.launchIn(lifecycleScope)


        lifecycleScope.launch(Dispatchers.Default) {
            while (true) {
                val data = dataQueue.take()
                val spo2Data = ArrayList<Int>(250)
                val pulseWaveData = ArrayList<Int>(120)
                data.forEachIndexed { index, data ->
                    val i = data.toInt()
                    when (index) {
                        in 4..253 -> spo2Data.add(i)
                        254 -> eventViewModel.respirationRate.emit(i)
                        255 -> eventViewModel.inhalationMark.emit(i)
                        in 256..375 -> pulseWaveData.add(i)
                        376 -> eventViewModel.spo2.emit(i)
                        377 -> eventViewModel.pulse.emit(i)
                    }
                }
                val async1 = async {
                    spo2Data.forEach {
                        eventViewModel.spo2Data.emit(it)
                        delay(4L)
                    }
                }
                val async2 = async {
                    pulseWaveData.forEach {
                        eventViewModel.pulseWaveData.emit(it)
                        delay(8L)
                    }
                }
                async1.await()
                async2.await()
                "next".loge("Csw")
            }
        }
    }

    fun start() {
        if (bleUtils.checkBle()) {
            val permissionList = ArrayList<String>()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                permissionList.add(Manifest.permission.BLUETOOTH_SCAN)
                permissionList.add(Manifest.permission.BLUETOOTH_ADVERTISE)
                permissionList.add(Manifest.permission.BLUETOOTH_CONNECT)
            }
            PermissionUtils.location(requireContext(), {
                if (permissionList.isNotEmpty())
                    PermissionX.init(this).permissions(
                        permissionList
                    ).request { allGranted, grantedList, list ->
                        if (allGranted)
                            scan()
                    }
                else
                    scan()

            })
        }

    }

    private fun scan() {
        showLoading("蓝牙搜索中")
        when {
            bleUtils.bleIsOpen().not() -> openBle()
            checkGPSIsOpen().not() -> openGPS()
            else -> bleUtils.startScan(MyBleScanCallback(this))
        }

    }

    private fun openBle() {
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        startActivityForResult(enableBtIntent, 1)
    }

    private fun checkGPSIsOpen(): Boolean {
        val locationManager = context?.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    }

    private fun openGPS() {
        AlertDialog.Builder(requireContext()).setMessage("开启GPS定位").setNegativeButton("取消", null)
            .setPositiveButton("打开") { _, _ ->
                val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                startActivityForResult(intent, 2)
            }.show()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> if (checkGPSIsOpen()) {
                bleUtils.startScan(MyBleScanCallback(this))
            } else {
                openGPS()
            }
            2 -> if (bleUtils.bleIsOpen()) {
                bleUtils.startScan(MyBleScanCallback(this))
            } else {
                openBle()
            }
        }
    }

    private fun writeDataToBle(data: ByteArray) {
        bleUtils.writeData(data)
    }

    class MyBleScanCallback(fragment: HomeFrag) : BleScanCallback() {
        private val myFragment: WeakReference<HomeFrag> = WeakReference(fragment)

        override fun onScanStarted(success: Boolean) {
            myFragment.get()?.mViewModel?.toast?.postValue("开始扫描")
        }

        override fun onScanning(result: BleDevice) {
            myFragment.get()?.mViewModel?.toast?.postValue("发现设备：${result.name}")
        }

        override fun onScanFinished(scanResultList: List<BleDevice>) {
            val bleDevice = scanResultList.firstOrNull() ?: return myFragment.get()?.dismissLoading() ?: Unit
            myFragment.get()?.dismissLoading()
            myFragment.get()?.showLoading("蓝牙连接中")
            myFragment.get()?.bleUtils?.connect(bleDevice, MyBleGattCallback(myFragment.get() ?: return))
        }
    }

    class MyBleGattCallback(fragment: HomeFrag) : BleGattCallback() {
        private val myFragment: WeakReference<HomeFrag> = WeakReference(fragment)

        override fun onStartConnect() {
            myFragment.get()?.mViewModel?.toast?.postValue("开始连接")
        }

        override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
            myFragment.get()?.mViewModel?.toast?.postValue("蓝牙连接失败")
        }

        override fun onConnectSuccess(
            bleDevice: BleDevice?,
            gatt: BluetoothGatt?,
            status: Int
        ) {
            myFragment.get()?.mViewModel?.toast?.postValue("连接成功")
            val services = gatt?.services
            myFragment.get()?.bleUtils?.getService(services)
            myFragment.get()?.onConnectSuccess()
        }

        override fun onDisConnected(
            isActiveDisConnected: Boolean,
            device: BleDevice?,
            gatt: BluetoothGatt?,
            status: Int
        ) {
            myFragment.get()?.onDisConnected()
        }

    }

    class MyBleNotifyCallback(fragment: HomeFrag) : BleNotifyCallback() {
        private val myFragment: WeakReference<HomeFrag> = WeakReference(fragment)
        override fun onNotifySuccess() {

        }

        override fun onNotifyFailure(exception: BleException?) {
        }

        override fun onCharacteristicChanged(data: ByteArray?) {
            data?.let {
                myFragment.get()?.decode(it)
            }
        }

    }

    private fun getCommon(): ByteArray {
        val settingBean = mViewModel.SettingBean.value!!
        val factorySettingBean = mViewModel.factorySettingData.value!!
        return byteArrayOf(
            0xff.toByte(),
            0,
            24,
            0x81.toByte(),
            settingBean.model.toByte(),
            time.toByte(),
            if (settingBean.autoOpen) 1 else 0,
            settingBean.volume.toByte(),
            0,
            0,
            0,
            factorySettingBean.hydrogenDisplacement.toByte(),
            factorySettingBean.curFunction.toByte(),
            factorySettingBean.language.toByte(),
            0,//用户累计时间 4字节
//            0,//用户累计时间 4字节
//            0,//用户累计时间 4字节
//            0,//用户累计时间 4字节
//            0,//厂家累计时间 6字节
//            0,//厂家累计时间 6字节
//            0,//厂家累计时间 6字节
//            0,//厂家累计时间 6字节
//            0,//厂家累计时间 6字节
//            0,//厂家累计时间 6字节
//            0,
//            0,
//            0//校验和
        )
    }

    private fun decode(it: ByteArray) {
        val type = it[3].toInt().and(0xff)
        when (type) {
            0x02 -> dataQueue.push(it)
        }

    }

    private fun onConnectSuccess() {
        dismissLoading()
        bleUtils.notifyCharacteristic(MyBleNotifyCallback(this))
        writeDataToBle(getCommon())
    }

    private fun onDisConnected() {

    }

    inner class Click {
        fun action() {
            if (mViewModel.isStart.value == false)
                start()
        }

        fun addTime() {
            if (time < 180)
                time += 30
        }

        fun minusTime() {
            if (time > 30)
                time -= 30
        }

        fun addFlow() {
            mViewModel.factorySettingData.value?.hydrogenDisplacement = mViewModel.factorySettingData.value?.hydrogenDisplacement!! + 100
            mViewModel.factorySettingData.value = mViewModel.factorySettingData.value
        }

        fun minusFlow() {
            if (mViewModel.factorySettingData.value?.hydrogenDisplacement!! > 0) {
                mViewModel.factorySettingData.value?.hydrogenDisplacement = mViewModel.factorySettingData.value?.hydrogenDisplacement!! - 100
                mViewModel.factorySettingData.value = mViewModel.factorySettingData.value
            }
        }

        fun setting() {
            nav().navigateAction(R.id.settingFrag)
        }
    }
}